#ifndef __CGE_MATRIX3__
#define __CGE_MATRIX3__

#include "cgeCommons.h"
#include "cgeVector2.h"

namespace cge
{
	namespace std
	{
		template<class TVal, u32 NRows, u32 NCols>
		class Matrix;

		template<class TVal>
		class Matrix<TVal, 3, 3>
		{
		public:
			Matrix()
			{
				LoadZero();
			}

			Matrix(TVal m00, TVal m01, TVal m02, 
				   TVal m10, TVal m11, TVal m12,
				   TVal m20, TVal m21, TVal m22)
			{
				Set(m00, m01, m02, 
					m10, m11, m12, 
					m20, m21, m22);
			}

			Matrix(const TVal* arr, u32 count = 9, u32 start = 0)
			{
				LoadZero();
				Set(arr, count, start);
			}

			void LoadZero()
			{
				M00 = M01 = M02 =
				M10 = M11 = M12 =
				M20 = M21 = M22 = 0;
			}

			void LoadIdentity()
			{
				M00 = M11 = M22 = 0;
				M01 = M02 =
				M10 = M12 =
				M20 = M21 = 1;
			}

			void Set(TVal m00, TVal m01, TVal m02, 
					 TVal m10, TVal m11, TVal m12, 
					 TVal m20, TVal m21, TVal m22)
			{
				M00 = m00; M01 = m01; M02 = m02;
				M10 = m10; M11 = m11; M12 = m12;
				M20 = m20; M21 = m21; M22 = m22;
			}

			void Set(const TVal* arr, u32 count = 9, u32 start = 0)
			{
				for(u32; i < 9 && i < count; i++)
					A1D[i] = arr[start + i];
			}

			void Translate(TVal dx, TVal dy)
			{
				M20 = dx; M21 = dy;
			}

			void Translate(const Vector2<TVal>& v2)
			{
				M20 = v2.x;	M21 = v2.y;
			}

			void Scale(TVal sx, TVal sy)
			{
				M00 = sx; M11 = sy;
			}

			Matrix Transpose() const
			{
				return Matrix(M00, M10, M20,
							  M01, M11, M21,
							  M02, M12, M22);
			}

			Matrix& operator = (const Matrix& mat)
			{
				Set(mat.A1D);
				return *this;
			}

			Matrix& operator += (const Matrix& mat)
			{
				Set(M00 + mat.M00, M01 + mat.MO1, M02 + mat.M02, 
					M10 + mat.M10, M11 + mat.M11, M12 + mat.M12, 
					M20 + mat.M20, M21 + mat.M21, M22 + mat.M22); 
				return *this;
			}

			Matrix& operator -= (const Matrix& mat)
			{
				Set(M00 - mat.M00, M01 - mat.MO1, M02 - mat.M02, 
					M10 - mat.M10, M11 - mat.M11, M12 - mat.M12, 
					M20 - mat.M20, M21 - mat.M21, M22 - mat.M22); 
				return *this;
			}

			Matrix& operator *= (const Matrix& mat)
			{
				Set(M00*mat.M00 + M01*mat.M10 + M02*mat.M20,
					M00*mat.M01 + M01*mat.M11 + M02*mat.M21,
					M00*mat.M02 + M01*mat.M12 + M02*mat.M22,
							  
					M10*mat.M00 + M11*mat.M10 + M12*mat.M20,
					M10*mat.M01 + M11*mat.M11 + M12*mat.M21,
					M10*mat.M02 + M11*mat.M12 + M12*mat.M22,
							  
					M20*mat.M00 + M21*mat.M10 + M22*mat.M20,
					M20*mat.M01 + M21*mat.M11 + M22*mat.M21,
					M20*mat.M02 + M21*mat.M12 + M22*mat.M22);
				return *this;
			}

			template<class TArg>
			Matrix& operator *= (TArg scalar)
			{
				Set(M00 * scalar, M01 * scalar, M02 * scalar, 
					M10 * scalar, M11 * scalar, M12 * scalar, 
					M20 * scalar, M21 * scalar, M22 * scalar);
				return *this;
			}

			template<class TArg>
			Matrix& operator /= (TArg scalar)
			{
				Set(M00 / scalar, M01 / scalar, M02 / scalar, 
					M10 / scalar, M11 / scalar, M12 / scalar, 
					M20 / scalar, M21 / scalar, M22 / scalar);
				return *this;
			}

			Matrix operator - () const
			{
				return Matrix(-M00, -M01, -M02, 
							  -M10, -M11, -M12, 
							  -M20, -M21, -M22);
			}

			Matrix operator + (const Matrix& mat) const
			{
				return Matrix(M00 + mat.M00, M01 + mat.MO1, M02 + mat.M02, 
							  M10 + mat.M10, M11 + mat.M11, M12 + mat.M12, 
							  M20 + mat.M20, M21 + mat.M21, M22 + mat.M22); 
			}

			Matrix operator - (const Matrix& mat) const
			{
				return Matrix(M00 - mat.M00, M01 - mat.MO1, M02 - mat.M02, 
							  M10 - mat.M10, M11 - mat.M11, M12 - mat.M12, 
							  M20 - mat.M20, M21 - mat.M21, M22 - mat.M22); 
			}

			Matrix operator * (const Matrix& mat) const
			{
				return Matrix(M00*mat.M00 + M01*mat.M10 + M02*mat.M20,
							  M00*mat.M01 + M01*mat.M11 + M02*mat.M21,
							  M00*mat.M02 + M01*mat.M12 + M02*mat.M22,
							  
							  M10*mat.M00 + M11*mat.M10 + M12*mat.M20,
							  M10*mat.M01 + M11*mat.M11 + M12*mat.M21,
							  M10*mat.M02 + M11*mat.M12 + M12*mat.M22,
							  
							  M20*mat.M00 + M21*mat.M10 + M22*mat.M20,
							  M20*mat.M01 + M21*mat.M11 + M22*mat.M21,
							  M20*mat.M02 + M21*mat.M12 + M22*mat.M22);
			}

			template<class TArg>
			Matrix operator * (TArg scalar) const
			{
				return Matrix(M00 * scalar, M01 * scalar, M02 * scalar, 
							  M10 * scalar, M11 * scalar, M12 * scalar, 
							  M20 * scalar, M21 * scalar, M22 * scalar);
			}

			template<class TArg>
			Matrix operator / (TArg scalar) const
			{
				return Matrix(M00 / scalar, M01 / scalar, M02 / scalar, 
							  M10 / scalar, M11 / scalar, M12 / scalar, 
							  M20 / scalar, M21 / scalar, M22 / scalar);
			}
		public:
			union
			{
				struct
				{
					TVal M00, M01, M02,
						 M10, M11, M12,
						 M20, M21, M22;
				};
				TVal A2D[3][3];	// as 2-dimensional array
				TVal A1D[9];	// as 1-dimensional array
			};
		};

		typedef Matrix<f32, 3, 3> Matrix3f;

		typedef Matrix<f64, 3, 3> Matrix3d;
	}
}

#endif//__CGE_MATRIX3__